/*
 * ScenarioLinkTypesDialog.java
 *
 * Created on November 11, 2008, 1:52 AM
 *
 * Copyright 2008 David D. Emory
 *
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 *
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.apps.admin.gui.dialogs;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.fpdev.core.Scenario;
import org.fpdev.core.basenet.BLink;
import org.fpdev.core.basenet.BLinkStreet;
import org.fpdev.apps.admin.AdminClient;
import org.fpdev.apps.admin.gui.LinkOptionsPanel;
import org.fpdev.util.gui.GUIFactory;

/**
 *
 * @author demory
 */
public class ScenarioLinkTypesDialog extends JDialog implements ActionListener {

  private AdminClient av_;

  private Scenario baseScen_, activeScen_;

  private Map<Short, ScenarioLinkInfo> scenOverrides_;

  private JList fullList_, selectedList_;
  private DefaultListModel fullListModel_, selectedListModel_;
  private JButton addScenBtn_, remScenBtn_;

  private JComboBox typeSel_, trnProvSel_, bikeProvSel_;

  private JButton applyBtn_, cancelBtn_;

  private boolean okPressed_;

  private int defaultType_, defaultTrnProv_, defaultBikeProv_;
  
  public ScenarioLinkTypesDialog(Set<BLinkStreet> links, AdminClient av) {
    super(av.getGUI(), "Scenario Specific Link Types", true);

    av_ = av;
    okPressed_ = false;

    scenOverrides_ = new HashMap<Short, ScenarioLinkInfo>();
    selectedListModel_ = new DefaultListModel();
    
    if(!checkBaseScenario(links)) return;

    findCommonOverrides(links);

    JPanel topRow = new JPanel();
    topRow.add(GUIFactory.newLabel("Changes will be applied to "+links.size()+" link"+(links.size() > 1 ? "s" : "")));

    JPanel center = new JPanel(new BorderLayout());

    JPanel selectorPanel = GUIFactory.newRowPanel();
    fullListModel_ = new DefaultListModel();
    fullList_ = new JList(fullListModel_);
    JPanel fullListPanel = new JPanel(new BorderLayout());
    fullListPanel.add(new JScrollPane(fullList_), BorderLayout.CENTER);
    fullListPanel.add(GUIFactory.newLabel("All child scenarios:"), BorderLayout.NORTH);
    //GUIFactory.setSize(fullListPanel, 160, 0);
    selectedList_ = new JList(selectedListModel_);
    selectedList_.addListSelectionListener(new ListSelectionListener() {
      public void valueChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting()) return;
        activeScenarioChanged((Scenario) selectedList_.getSelectedValue());
      }
    });
    JPanel selectedListPanel = new JPanel(new BorderLayout());
    selectedListPanel.add(new JScrollPane(selectedList_), BorderLayout.CENTER);
    selectedListPanel.add(GUIFactory.newLabel("Scenario overrides:"), BorderLayout.NORTH);
    JPanel btnCol = GUIFactory.newColumnPanel();
    addScenBtn_ = GUIFactory.newButton(">", 25, this);
    remScenBtn_ = GUIFactory.newButton("<", 25, this);
    btnCol.add(addScenBtn_);
    btnCol.add(Box.createVerticalStrut(5));
    btnCol.add(remScenBtn_);
    selectorPanel.add(fullListPanel);
    selectorPanel.add(Box.createHorizontalStrut(4));
    selectorPanel.add(btnCol);
    selectorPanel.add(Box.createHorizontalStrut(4));
    selectorPanel.add(selectedListPanel);
    initListContents();

    JPanel settingsPanel = GUIFactory.newColumnPanel();
    // base type dropdown
    settingsPanel.add(GUIFactory.newLabel("Base type:", LEFT_ALIGNMENT));
    typeSel_ = LinkOptionsPanel.createLinkTypeComboBox();
    typeSel_.addItem("0. No link");
    typeSel_.addActionListener(this);
    typeSel_.setEnabled(false);
    typeSel_.setAlignmentX(LEFT_ALIGNMENT);
    settingsPanel.add(typeSel_);
    settingsPanel.add(Box.createVerticalStrut(5));
    // transit provisions dropdown
    settingsPanel.add(GUIFactory.newLabel("Transit Provisions:", LEFT_ALIGNMENT));
    trnProvSel_ = LinkOptionsPanel.createTransitProvisionsComboBox();
    trnProvSel_.addActionListener(this);
    trnProvSel_.setEnabled(false);
    trnProvSel_.setAlignmentX(LEFT_ALIGNMENT);
    settingsPanel.add(trnProvSel_);
    settingsPanel.add(Box.createVerticalStrut(5));
    // bike provisions dropdown
    settingsPanel.add(GUIFactory.newLabel("Bike Provisions:", LEFT_ALIGNMENT));
    bikeProvSel_ = LinkOptionsPanel.createBikeProvisionsComboBox();
    bikeProvSel_.addActionListener(this);
    bikeProvSel_.setEnabled(false);
    bikeProvSel_.setAlignmentX(LEFT_ALIGNMENT);
    settingsPanel.add(bikeProvSel_);
    settingsPanel.setBorder(GUIFactory.createPaddedTitledBorder("Scenario-Based Settings", 5));
    
    center.add(selectorPanel, BorderLayout.CENTER);
    center.add(settingsPanel, BorderLayout.SOUTH);


    // lower button row
    JPanel buttonRow = new JPanel();
    buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
    applyBtn_ = GUIFactory.newButton("Apply Changes", 120, this);
    applyBtn_.setEnabled(false);
    cancelBtn_ = GUIFactory.newButton("Cancel", 80, this);
    buttonRow.add(Box.createHorizontalGlue());
    buttonRow.add(applyBtn_);
    buttonRow.add(Box.createHorizontalStrut(5));
    buttonRow.add(cancelBtn_);
    buttonRow.add(Box.createHorizontalGlue());
    buttonRow.setBorder(new EmptyBorder(4,0,0,0));

    // main panel
    JPanel mainPanel = new JPanel(new BorderLayout());
    mainPanel.add(topRow, BorderLayout.NORTH);
    mainPanel.add(center, BorderLayout.CENTER);
    mainPanel.add(buttonRow, BorderLayout.SOUTH);
    mainPanel.setBorder(new EmptyBorder(5,5,5,5));

    getContentPane().add(mainPanel);
    setSize(350,350);
    setLocationRelativeTo(null);
    setVisible(true);
  }

  public void actionPerformed(ActionEvent e) {
    if(e.getSource() == applyBtn_) okPressed_ = true;
    if(e.getSource() == applyBtn_ || e.getSource()==cancelBtn_) setVisible(false);

    if(e.getSource() == addScenBtn_) addScenario();
    if(e.getSource() == remScenBtn_) removeScenario();

    if(e.getSource() == typeSel_) linkTypeProvisionChanged();
    if(e.getSource() == trnProvSel_) transitProvisionChanged();
    if(e.getSource() == bikeProvSel_) bikeProvisionChanged();

  }

  public boolean okPressed() {
    return okPressed_;
  }

  public Map<Short, Integer> getOverrides() {
    Map<Short, Integer> overrides = new HashMap<Short, Integer>();
    for(Iterator<Entry<Short,ScenarioLinkInfo>> entries = scenOverrides_.entrySet().iterator(); entries.hasNext(); ) {
      Entry<Short,ScenarioLinkInfo> entry = entries.next();
      overrides.put(entry.getKey(), entry.getValue().toFullType());
    }
    return overrides;
  }

  /**
   * Checks to see if all of the links belong to a common base scenario. This
   * is required since only the descendents of a given scenario are applicable
   * as potential type-overriding scenarios.
   * 
   * @param links
   * @return boolean indicating whether the links share a common scenario
   */
  private boolean checkBaseScenario(Set<BLinkStreet> links) {
    Set<Scenario> scens = new HashSet<Scenario>();
    for (BLink link : links) scens.add(link.getScenario());
    if(scens.size() != 1) {
      av_.msg("Cannot edit scenario-specific link types; selected links must all share a common scenario");
      return false;
    }
    baseScen_ = scens.iterator().next();
    return true;
  }

  private void findCommonOverrides(Set<BLinkStreet> links) {
    Set<String> common = null;
    for (BLinkStreet link : links) {
      Set<String> current = new HashSet<String>();
      Map<Short, Integer> overrides = av_.getEngine().getBaseNet().getScenLinkOverrides(link.getID());
      if(overrides != null) {
        for(Map.Entry<Short,Integer> entry : overrides.entrySet())
          current.add(entry.getKey()+"_"+entry.getValue());
      }
      if(common == null) {
        common = new HashSet<String>();
        common.addAll(current);
        defaultType_ = link.getType();
        defaultTrnProv_ = link.getTransitProv();
        defaultBikeProv_ = link.getBikePedProv();
      }
      else {
        common.retainAll(current);
        defaultType_ = (link.getType() == defaultType_) ? defaultType_ : 30;
        defaultTrnProv_ = (link.getTransitProv() == defaultTrnProv_) ? defaultTrnProv_ : 0;
        defaultBikeProv_ = (link.getBikePedProv() == defaultBikeProv_) ? defaultBikeProv_ : 0;
      }

    }
    for (String str : common) {
      String[] split = str.split("_");
      short scenID = Short.parseShort(split[0]);
      int extType = Integer.parseInt(split[1]);
      scenOverrides_.put(scenID, new ScenarioLinkInfo(extType));
      selectedListModel_.addElement(av_.getEngine().getScenarios().get(scenID));
    }
  }

  private void initListContents() {
    Set<Scenario> descendants = baseScen_.getDescendents();
    //System.out.println("desc count = "+descendants.size());
    //childScens_ = new Scenario[descendants.size()];

    //int i = 0;
    for (Scenario s: descendants) {
      fullListModel_.addElement(s);
      //childScens_[i] = s;
      //i++;
    }
  }

  private void addScenario() {
    Scenario s = (Scenario) fullList_.getSelectedValue();
    if(s == null || selectedListModel_.contains(s)) return;
    selectedListModel_.addElement(s); //.getName());
    scenOverrides_.put(s.getID(), new ScenarioLinkInfo());
    applyBtn_.setEnabled(true);
  }

  private void removeScenario() {
    int i = selectedList_.getSelectedIndex();
    if(i == -1) return;
    
    Scenario s = (Scenario) selectedList_.getSelectedValue();
    if(activeScen_ == s) {
      typeSel_.setSelectedIndex(0);
      typeSel_.setEnabled(false);
      trnProvSel_.setSelectedIndex(0);
      trnProvSel_.setEnabled(false);
      bikeProvSel_.setSelectedIndex(0);
      bikeProvSel_.setEnabled(false);      
    }
    selectedListModel_.remove(i);
    scenOverrides_.remove(s.getID());
    applyBtn_.setEnabled(true);
  }

  private void linkTypeProvisionChanged() {
    int type = Integer.parseInt(typeSel_.getSelectedItem().toString().split("\\.")[0]);
    if(type == 0) {
      trnProvSel_.setSelectedIndex(0);
      trnProvSel_.setEnabled(false);
      bikeProvSel_.setSelectedIndex(0);
      bikeProvSel_.setEnabled(false);
    }
    else {
      trnProvSel_.setEnabled(true);
      bikeProvSel_.setEnabled(true);
    }
    scenOverrides_.get(activeScen_.getID()).type_ = type;
    applyBtn_.setEnabled(true);
  }

  private void transitProvisionChanged() {
    int tprov = Integer.parseInt(trnProvSel_.getSelectedItem().toString().split("\\.")[0]);
    scenOverrides_.get(activeScen_.getID()).trnProv_ = tprov;
    applyBtn_.setEnabled(true);
  }

  private void bikeProvisionChanged() {
    int bprov = Integer.parseInt(bikeProvSel_.getSelectedItem().toString().split("\\.")[0]);
    scenOverrides_.get(activeScen_.getID()).bikeProv_ = bprov;
    applyBtn_.setEnabled(true);
  }

  private void activeScenarioChanged(Scenario scenario) {
    if(scenario == null) return;
    
    activeScen_ = scenario;
    typeSel_.setEnabled(true);
    trnProvSel_.setEnabled(true);
    bikeProvSel_.setEnabled(true);

    ScenarioLinkInfo info = scenOverrides_.get(scenario.getID());
    //System.out.println("Setting type CB to "+info.type_);
    setCBSelectionFromID(typeSel_, info.type_);
    //System.out.println("Setting bike CB to "+info.bikeProv_);
    setCBSelectionFromID(bikeProvSel_, info.bikeProv_);
    setCBSelectionFromID(trnProvSel_, info.trnProv_);
  }

  private void setCBSelectionFromID(JComboBox cb, int id) {
    for (int i = 0; i < cb.getItemCount(); i++) {
      String s = cb.getItemAt(i).toString();
      //if(!s.contains(".")) continue;
      if(Integer.parseInt(s.split("\\.")[0]) == id) {
        cb.setSelectedIndex(i);
        return;
      }
    }
  }

  public class ScenarioLinkInfo {
    private int type_, trnProv_, bikeProv_;

    public ScenarioLinkInfo() {
      this(defaultType_, defaultTrnProv_, defaultBikeProv_);
    }

    public ScenarioLinkInfo(int extType) {
      this(extType/100, extType/10 % 10, extType % 10);
    }

    public ScenarioLinkInfo(int type, int trnProv, int bikeProv) {
      type_= type;
      trnProv_ = trnProv;
      bikeProv_ = bikeProv;
    }

    public int toFullType() {
      return type_*100 + trnProv_*10 + bikeProv_;
    }
  }
}
